home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / File / Passwd.php < prev    next >
PHP Script  |  2004-10-01  |  14KB  |  413 lines

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PEAR :: File :: Passwd                                               |
  4. // +----------------------------------------------------------------------+
  5. // | This source file is subject to version 3.0 of the PHP license,       |
  6. // | that is available at http://www.php.net/license/3_0.txt              |
  7. // | If you did not receive a copy of the PHP license and are unable      |
  8. // | to obtain it through the world-wide-web, please send a note to       |
  9. // | license@php.net so we can mail you a copy immediately.               |
  10. // +----------------------------------------------------------------------+
  11. // | Copyright (c) 2003-2004 Michael Wallner <mike@iworks.at>             |
  12. // +----------------------------------------------------------------------+
  13. //
  14. // $Id: Passwd.php,v 1.21 2004/03/16 14:13:27 mike Exp $
  15.  
  16. /**
  17. * Manipulate many kinds of passwd files.
  18. * @author       Michael Wallner <mike@php.net>
  19. * @package      File_Passwd
  20. * @category     FileSystem
  21. */
  22.  
  23. /**
  24. * Requires PEAR.
  25. */
  26. require_once 'PEAR.php';
  27.  
  28. /**
  29. * Encryption constants.
  30. */
  31. // SHA encryption.
  32. define('FILE_PASSWD_SHA',   'sha');
  33. // MD5 encryption
  34. define('FILE_PASSWD_MD5',   'md5');
  35. // DES encryption
  36. define('FILE_PASSWD_DES',   'des');
  37. // NT hash encryption.
  38. define('FILE_PASSWD_NT',    'nt');
  39. // LM hash encryption.
  40. define('FILE_PASSWD_LM',    'lm');
  41. // PLAIN (no encryption)
  42. define('FILE_PASSWD_PLAIN', 'plain');
  43.  
  44. /**
  45. * Error constants.
  46. */
  47. // Undefined error.
  48. define('FILE_PASSWD_E_UNDEFINED',                   0);
  49. // Invalid file format.
  50. define('FILE_PASSWD_E_INVALID_FORMAT',              1);
  51. define('FILE_PASSWD_E_INVALID_FORMAT_STR',          'Passwd file has invalid format.');
  52. // Invalid extra property.
  53. define('FILE_PASSWD_E_INVALID_PROPERTY',            2);
  54. define('FILE_PASSWD_E_INVALID_PROPERTY_STR',        'Invalid property \'%s\'.');
  55. // Invalid characters.
  56. define('FILE_PASSWD_E_INVALID_CHARS',               3);
  57. define('FILE_PASSWD_E_INVALID_CHARS_STR',           '%s\'%s\' contains illegal characters.');
  58. // Invalid encryption mode.
  59. define('FILE_PASSWD_E_INVALID_ENC_MODE',            4);
  60. define('FILE_PASSWD_E_INVALID_ENC_MODE_STR',        'Encryption mode \'%s\' not supported.');
  61. // Exists already.
  62. define('FILE_PASSWD_E_EXISTS_ALREADY',              5);
  63. define('FILE_PASSWD_E_EXISTS_ALREADY_STR',          '%s\'%s\' already exists.');
  64. // Exists not.
  65. define('FILE_PASSWD_E_EXISTS_NOT',                  6);
  66. define('FILE_PASSWD_E_EXISTS_NOT_STR',              '%s\'%s\' doesn\'t exist.');
  67. // User not in group.
  68. define('FILE_PASSWD_E_USER_NOT_IN_GROUP',           7);
  69. define('FILE_PASSWD_E_USER_NOT_IN_GROUP_STR',       'User \'%s\' doesn\'t exist in group \'%s\'.');
  70. // User not in realm.
  71. define('FILE_PASSWD_E_USER_NOT_IN_REALM',           8);
  72. define('FILE_PASSWD_E_USER_NOT_IN_REALM_STR',       'User \'%s\' doesn\'t exist in realm \'%s\'.');
  73. // Parameter must be of type array.
  74. define('FILE_PASSWD_E_PARAM_MUST_BE_ARRAY',         9);
  75. define('FILE_PASSWD_E_PARAM_MUST_BE_ARRAY_STR',     'Parameter %s must be of type array.');
  76. // Method not implemented.
  77. define('FILE_PASSWD_E_METHOD_NOT_IMPLEMENTED',      10);
  78. define('FILE_PASSWD_E_METHOD_NOT_IMPLEMENTED_STR',  'Method \'%s()\' not implemented.');
  79. // Directory couldn't be created.
  80. define('FILE_PASSWD_E_DIR_NOT_CREATED',             11);
  81. define('FILE_PASSWD_E_DIR_NOT_CREATED_STR',         'Couldn\'t create directory \'%s\'.');
  82. // File couldn't be opened.
  83. define('FILE_PASSWD_E_FILE_NOT_OPENED',             12);
  84. define('FILE_PASSWD_E_FILE_NOT_OPENED_STR',         'Couldn\'t open file \'%s\'.');
  85. // File coudn't be locked.
  86. define('FILE_PASSWD_E_FILE_NOT_LOCKED',             13);
  87. define('FILE_PASSWD_E_FILE_NOT_LOCKED_STR',         'Couldn\'t lock file \'%s\'.');
  88. // File couldn't be unlocked.
  89. define('FILE_PASSWD_E_FILE_NOT_UNLOCKED',           14);
  90. define('FILE_PASSWD_E_FILE_NOT_UNLOCKED_STR',       'Couldn\'t unlock file.');
  91. // File couldn't be closed.
  92. define('FILE_PASSWD_E_FILE_NOT_CLOSED',             15);
  93. define('FILE_PASSWD_E_FILE_NOT_CLOSED_STR',         'Couldn\'t close file.');
  94.  
  95. /**
  96. * Allowed 64 chars for salts
  97. */
  98. $GLOBALS['_FILE_PASSWD_64'] =
  99.     './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  100.  
  101. /**
  102. * The package File_Passwd provides classes and methods 
  103. * to handle many different kinds of passwd files.
  104. * The File_Passwd class in certain is a factory container for its special 
  105. * purpose extension classes, each handling a specific passwd file format.
  106. * It also provides a static method for reasonable fast user authentication.
  107. * Beside that it offers some encryption methods used by the extensions.
  108. *
  109. * @author       Michael Wallner <mike@php.net>
  110. * @version      $Revision: 1.21 $
  111. * Usage Example:
  112. * <code>
  113. *  $passwd = &File_Passwd::factory('Unix');
  114. * </code>
  115. */
  116. class File_Passwd
  117. {
  118.     /**
  119.     * Get API version
  120.     *
  121.     * @static
  122.     * @access   public
  123.     * @return   string          API version
  124.     */
  125.     function apiVersion()
  126.     {
  127.         return '1.0.0';
  128.     }
  129.  
  130.     /**
  131.     * Generate salt
  132.     *
  133.     * @access   public
  134.     * @return   mixed
  135.     * @param    int     $length     salt length
  136.     * @return   string  the salt
  137.     */
  138.     function salt($length = 2)
  139.     {
  140.         $salt = '';
  141.         $length = (int) $length;
  142.         $length < 2 && $length = 2;
  143.         for($i = 0; $i < $length; $i++) {
  144.             $salt .= $GLOBALS['_FILE_PASSWD_64'][rand(0, 63)];
  145.         }
  146.         return $salt;
  147.     }
  148.  
  149.     /**
  150.     * No encryption (plaintext)
  151.     *
  152.     * @access   public
  153.     * @return   string  plaintext input
  154.     * @param    string  plaintext passwd
  155.     */
  156.     function crypt_plain($plain)
  157.     {
  158.         return $plain;
  159.     }
  160.     
  161.     /**
  162.     * DES encryption
  163.     *
  164.     * @static
  165.     * @access   public
  166.     * @return   string  crypted text
  167.     * @param    string  $plain  plaintext to encrypt
  168.     * @param    string  $salt   the salt to use for encryption (2 chars)
  169.     */
  170.     function crypt_des($plain, $salt = null)
  171.     {
  172.         (is_null($salt) || strlen($salt) < 2) && $salt = File_Passwd::salt(2);
  173.         return crypt($plain, $salt);
  174.     }
  175.     
  176.     /**
  177.     * MD5 encryption
  178.     *
  179.     * @static
  180.     * @access   public
  181.     * @return   string  crypted text
  182.     * @param    string  $plain  plaintext to encrypt
  183.     * @param    string  $salt   the salt to use for encryption 
  184.     *                           (>2 chars starting with $1$)
  185.     */
  186.     function crypt_md5($plain, $salt = null)
  187.     {
  188.         if (
  189.             is_null($salt) || 
  190.             strlen($salt) < 3 || 
  191.             !preg_match('/^\$1\$/', $salt))
  192.         {
  193.             $salt = '$1$' . File_Passwd::salt(8);
  194.         }
  195.         return crypt($plain, $salt);
  196.     }
  197.     
  198.     /**
  199.     * SHA1 encryption
  200.     *
  201.     * Returns a PEAR_Error if sha1() is not available (PHP<4.3).
  202.     * 
  203.     * @static
  204.     * @throws   PEAR_Error
  205.     * @access   public
  206.     * @return   mixed   crypted string or PEAR_Error
  207.     * @param    string  $plain  plaintext to encrypt
  208.     */
  209.     function crypt_sha($plain)
  210.     {
  211.         if (!function_exists('sha1')) {
  212.             return PEAR::raiseError(
  213.                 'SHA1 encryption is not available (PHP < 4.3).',
  214.                 FILE_PASSWD_E_INVALID_ENC_MODE
  215.             );
  216.         }
  217.         return '{SHA}' . base64_encode(
  218.             File_Passwd::_hexbin(sha1($plain))
  219.         );
  220.  
  221.     }
  222.         
  223.     /**
  224.     * APR compatible MD5 encryption
  225.     *
  226.     * @access   public
  227.     * @return   mixed
  228.     * @param    string  $plain  plaintext to crypt
  229.     * @param    string  $salt   the salt to use for encryption
  230.     */
  231.     function crypt_apr_md5($plain, $salt = null)
  232.     {
  233.         if (is_null($salt)) {
  234.             $salt = File_Passwd::salt(8);
  235.         } elseif (preg_match('/^\$apr1\$/', $salt)) {
  236.             $salt = preg_replace('/^\$apr1\$([^$]+)\$.*/', '\\1', $salt);
  237.         } else {
  238.             $salt = substr($salt, 0,8);
  239.         }
  240.         
  241.         $length     = strlen($plain);
  242.         $context    = $plain . '$apr1$' . $salt;
  243.         $binary     = File_Passwd::_hexbin(md5($plain . $salt . $plain));
  244.         
  245.         for ($i = $length; $i > 0; $i -= 16) {
  246.             $context .= substr($binary, 0, ($i > 16 ? 16 : $i));
  247.         }
  248.         for ( $i = $length; $i > 0; $i >>= 1) {
  249.             $context .= ($i & 1) ? chr(0) : $plain[0];
  250.         }
  251.         
  252.         $binary = File_Passwd::_hexbin(md5($context));
  253.         
  254.         for($i = 0; $i < 1000; $i++) {
  255.             $new = ($i & 1) ? $plain : substr($binary, 0,16);
  256.             if ($i % 3) {
  257.                 $new .= $salt;
  258.             }
  259.             if ($i % 7) {
  260.                 $new .= $plain;
  261.             }
  262.             $new .= ($i & 1) ? substr($binary, 0,16) : $plain;
  263.             $binary = File_Passwd::_hexbin(md5($new));
  264.         }
  265.         
  266.         $p = array();
  267.         for ($i = 0; $i < 5; $i++) {
  268.             $k = $i + 6;
  269.             $j = $i + 12;
  270.             if ($j == 16) {
  271.                 $j = 5;
  272.             }
  273.             $p[] = File_Passwd::_64(
  274.                 (ord($binary[$i]) << 16) |
  275.                 (ord($binary[$k]) << 8) |
  276.                 (ord($binary[$j])),
  277.                 5
  278.             );
  279.         }
  280.         
  281.         return 
  282.             '$apr1$' . $salt . '$' . implode($p) . 
  283.             File_Passwd::_64(ord($binary[11]), 3);
  284.     }
  285.  
  286.     /**
  287.     * Convert hexadecimal string to binary data
  288.     *
  289.     * @static
  290.     * @access   private
  291.     * @return   mixed
  292.     * @param    string  $hex
  293.     */
  294.     function _hexbin($hex)
  295.     {
  296.         $rs = '';
  297.         $ln = strlen($hex);
  298.         for($i = 0; $i < $ln; $i += 2) {
  299.             $rs .= chr(hexdec($hex{$i} . $hex{$i+1}));
  300.         }
  301.         return $rs;
  302.     }
  303.     
  304.     /**
  305.     * Convert to allowed 64 characters for encryption
  306.     *
  307.     * @static
  308.     * @access   private
  309.     * @return   string
  310.     * @param    string  $value
  311.     * @param    int     $count
  312.     */
  313.     function _64($value, $count)
  314.     {
  315.         $result = '';
  316.         while(--$count) {
  317.             $result .= $GLOBALS['_FILE_PASSWD_64'][$value & 0x3f];
  318.             $value >>= 6;
  319.         }
  320.         return $result;
  321.     }
  322.  
  323.     /**
  324.     * Factory for new extensions
  325.     * 
  326.     * o Unix        for standard Unix passwd files
  327.     * o CVS         for CVS pserver passwd files
  328.     * o SMB         for SMB server passwd files
  329.     * o Authbasic   for AuthUserFiles
  330.     * o Authdigest  for AuthDigestFiles
  331.     * o Custom      for custom formatted passwd files
  332.     * 
  333.     * Returns a PEAR_Error if the desired class/file couldn't be loaded.
  334.     * 
  335.     * @static   use &File_Passwd::factory() for instantiating your passwd object
  336.     * 
  337.     * @throws   PEAR_Error
  338.     * @access   public
  339.     * @return   object    File_Passwd_$class - desired Passwd object
  340.     * @param    string    $class the desired subclass of File_Passwd
  341.     */
  342.     function &factory($class)
  343.     {
  344.         $class = ucFirst(strToLower($class));
  345.         if (!@include_once "File/Passwd/$class.php") {
  346.             return PEAR::raiseError("Couldn't load file Passwd/$class.php", 0);
  347.         }
  348.         $class = 'File_Passwd_'.$class;
  349.         if (!class_exists($class)) {
  350.             return PEAR::raiseError("Couldn't load class $class.", 0);
  351.         }
  352.         $instance = &new $class();
  353.         return $instance;
  354.     }
  355.     
  356.     /**
  357.     * Fast authentication of a certain user
  358.     * 
  359.     * Though this approach should be reasonable fast, it is NOT
  360.     * with APR compatible MD5 encryption used for htpasswd style
  361.     * password files encrypted in MD5. Generating one MD5 password
  362.     * takes about 0.3 seconds!
  363.     * 
  364.     * Returns a PEAR_Error if:
  365.     *   o file doesn't exist
  366.     *   o file couldn't be opened in read mode
  367.     *   o file couldn't be locked exclusively
  368.     *   o file couldn't be unlocked (only if auth fails)
  369.     *   o file couldn't be closed (only if auth fails)
  370.     *   o invalid <var>$type</var> was provided
  371.     *   o invalid <var>$opt</var> was provided
  372.     * 
  373.     * Depending on <var>$type</var>, <var>$opt</var> should be:
  374.     *   o Smb:          encryption method (NT or LM)
  375.     *   o Unix:         encryption method (des or md5)
  376.     *   o Authbasic:    encryption method (des, sha or md5)
  377.     *   o Authdigest:   the realm the user is in
  378.     *   o Cvs:          n/a (empty)
  379.     *   o Custom:       array of 2 elements: encryption function and delimiter
  380.     * 
  381.     * @static   call this method statically for a reasonable fast authentication
  382.     * 
  383.     * @throws   PEAR_Error
  384.     * @access   public
  385.     * @return   return      mixed   true if authenticated, 
  386.     *                               false if not or PEAR_error
  387.     * 
  388.     * @param    string      $type   Unix, Cvs, Smb, Authbasic or Authdigest
  389.     * @param    string      $file   path to passwd file
  390.     * @param    string      $user   the user to authenticate
  391.     * @param    string      $pass   the plaintext password
  392.     * @param    mixed       $opt    o Smb:          NT or LM
  393.     *                               o Unix:         des or md5
  394.     *                               o Authbasic     des, sha or md5
  395.     *                               o Authdigest    realm the user is in
  396.     *                               o Custom        encryption function and
  397.     *                                               delimiter character
  398.     */
  399.     function staticAuth($type, $file, $user, $pass, $opt = '')
  400.     {
  401.         $type = ucFirst(strToLower($type));
  402.         if (!@include_once "File/Passwd/$type.php") {
  403.             return PEAR::raiseError("Couldn't load file Passwd/$type.php", 0);
  404.         }
  405.         $func = array('File_Passwd_' . $type, 'staticAuth');
  406.         return call_user_func($func, $file, $user, $pass, $opt);
  407.     }
  408. }
  409. ?>
  410.